<?php
namespace App\Common\Lib\Db;

use App\Common as com;
use think\facade\App;

/**
 * 数据表连接
 * 版本：v1.0.5
 * 更新日志：
 * 2022年11月19日18:13:20
 * 	1.变量命名统一转为短驼峰
 * 2022年10月27日17:44:22
 * 	1.修复了B连接id是否包含的bug
 * 2020年10月6日17:13:39
 * 	1.join_one支持直接传入数据
 * 2019年7月10日11:42:52
 * 	1.修改join_data_type默认类型为array
 * 	2....
 * 2018年4月17日11:47:37:
 * 	1.增加"isAutoDeleteSourceOfJoinEmpty"方法(已被其他人删除)
 * 2018年3月21日16:22:12
 * 注：仅适合简单的数据join，复杂的查询或数据处理，建议使用数据库的JOIN操作
 */
class Join extends com\Lib\LibBase {
	protected array $sourceData = []; //源数据
	protected array $joinResData = []; //join结果数据
	protected array $otherData = []; //其他数据

	protected array $joinConfig = [
		// 'join_table_name' => null, // 连接表名
		'joinTableInfo' => null, // 连接表名类
		'joinField' => '*', // 需要获取的连接表的字段，默认全部字段
		'atob' => [ 'id', 'id' ], // 连接条件[ 示例：[ 'id', 'id'] [默认为：['id', 'id'] ]
		// 'bFieldPrefix' => '_mainTable.', // B表字段前缀
		'echoSite' => null, // 连接数据输出位置（默认为"join_{atob[0]}）
		'defaultData' => null, // joinOne默认连接数据（如果连接的数据为空，则使用默认数据）
		'defaultDatas' => [], // joinMore默认连接数据（如果连接的数据为空，则使用默认数据）
		'sourceOne' => null, // 源数据是否为单维数组 [0]不是（多维数组） [1]是
		'showJoinSql' => 0, // 是否输出join的sql。[0]否 [1]是
		'isConnectOnlyRead' => false, // 是否使用从数据库查询。[false][默认]否 [1]是
		'joinOne' => 0, // 是否连接多条数据。[0]否 [1]是
		'showData' => 0, // 是否直接输出数据到浏览器。[0]否 [1]直接输出到浏览器
		'filterSourceJoinDataCallFun' => null, // 筛选"需要join的源数据"回调函数
		// [取消]'filter_source_join_data_default_data' => null, // 不允许"join的源数据"时的默认数据，被过滤掉时的默认值（如果数据已存在，则不覆盖）
		// [未做]'filter_source_join_data_force_default_data' => false, // 不允许"join的源数据"时的默认数据，被过滤掉时的默认值（如果数据已存在，是否强制覆盖）
		'useBIdToJoinArrayKey' => false, // 使用 b id 作为 join 数组键名。[false][默认]否 [true]是
		'joinTableQueryCallFun' => null, //连接表query回调函数
		'joinDataType' => 'array', // 返回数据类型 ['obj']对象 ['array'][默认]数组
		'ignoreNoExistA' => '0', // 是否忽略待连接表A中不存在的值 [0][默认]否，不存在则报错（抛出异常）[1]是
		'isDeleteSourceOfJoinEmpty' => false, // 是否自动删除join数据为空的源数据。[false][默认]否 [1]是
		'isDeleteAQuoteId' => false, // 是否删除A表引用id
		'isDeleteBQuoteId' => true, // 是否删除B表引用id
		'joinBDataCallfun' => true, // 单条B表数据回调
	];

	/**
	 * Join constructor.
	 * @param array|null $sourceData
	 */
	public function __construct( array|null $sourceData = null ){
		//
		if( is_array( $sourceData ) ){
			$this->sourceData( $sourceData );
		}
		//
		parent::__construct();
	}

	/**
	 * 初始化 SourceData 参数
	 * @param array $sourceData 原数据
	 * @param string 连接关系绑定->源字段
	 * @param bool $isMore 原数据是否为多行数据。[false][默认]否 [true]是
	 * @return $this
	 */
	public function initSourceDataParam( $sourceData, $atobA = null, bool $isMore = false ):self {
		// ...
		$this->sourceData( $sourceData );
		//传递参数
		if( $atobA !== null ) $this->setJoinFieldA( $atobA );
		$this->sourceMore( $isMore );
		//
		return $this;
	}

	/**
	 * 设置"筛选'需要join的源数据'回调函数"
	 * @param \Closure $callFun 回调函数（自动传入一个$data数据，回调函数返回true或null表示允许join，false表示不允许join）
	 * @param array $default_data 被过滤掉时的默认值（如果数据已存在，则不覆盖）（注：被过滤的数据行，不使用 $joinConfig->default_data）
	 * @return $this
	 */
	public function filterSourceJoinData( \Closure $callFun ){
		$this->joinConfig['filterSourceJoinDataCallFun'] = $callFun;
		return $this;
	}

	/**
	 * 设置"使用 b id 作为 join 数组键名"
	 * @param bool $isUse 是否使用
	 * @return $this
	 */
	public function setUseBIdToArrayKey( bool $isUse = true ){
		$this->joinConfig['useBIdToJoinArrayKey'] = $isUse;
		return $this;
	}

	/**
	 * 筛选出需要join的数据
	 * @return bool 是否需要进行筛选。[false]不需要 [true]需要
	 */
	protected function isFilterSourceJoinData( $rootData, $valueData ):bool {
		if( $this->joinConfig['filterSourceJoinDataCallFun'] === null ){
			return true;
		}
		//执行查询闭包
		return $this->joinConfig['filterSourceJoinDataCallFun']( $rootData, $valueData );
	}

	/**
	 * 是否存在'需要join的源数据'回调函数
	 * @return bool 是否需要进行筛选。[false]不需要 [true]需要
	 */
	protected function hasCallFilterSourceJoinData(){
		if( $this->joinConfig['filterSourceJoinDataCallFun'] instanceof \Closure ){
			return true;
		}else{
			return false;
		}
	}

	/**
	 * 需要连接的数据，如果不需要连接则返回默认值
	 * @param array $data 源数据的其中一行
	 * @return bool|mixed 是否需要join。[false]不需要 [{mixed}]默认数据
	 */
	/*	protected function getNeedSourceJoinDataAndDefaultData( $rootData, $valueData ){
			//执行查询闭包
			if( $this->joinConfig['filterSourceJoinDataCallFun'] instanceof \Closure ){
				return $this->joinConfig['filterSourceJoinDataCallFun']( $rootData, $valueData );
			}else{
				return true;
			}
		}*/

	/**
	 * 创建新join对象（原数据）
	 * @param array $sourceData 原数据
	 * @param string 连接关系绑定->源字段
	 * @param bool $isMore 原数据是否为多行数据。[false][默认]否 [true]是
	 * @return $this
	 */
	public static function newClassOfSourceData( $sourceData, $atobA = null, bool $isMore = false ){
		// ...
		// $is_more = $is_more ?? null;
		// 自动将对象转换成数组
		////$source_data = com\Lib\Fun\Base::toArray( $source_data );
		/*
		if( $is_more == true && count( $source_data ) > 0 && is_object( reset( $source_data ) ) ){
			$source_data = com\Lib\Fun\Base::toArray( $source_data );
		}
		*/
		// if( $is_more == false && is_object( $source_data ) ) $source_data = com\Lib\Fun\Base::toArray( $source_data );
		//// if( is_object( $source_data ) ) $source_data = com\Lib\Fun\Base::toArray( $source_data );
		//创建类
		// $this_class_name = ::class;
		$new_this = new static();
		$new_this->initSourceDataParam( $sourceData, $atobA, $isMore );
		/*		//传递参数
				$new_this->sourceMore( $is_more );
				if( $atob_a !== null ) $new_this->setJoinFieldA( $atob_a );*/
		//
		return $new_this;
	}

	/**
	 * 原数据
	 * @param array $sourceData 原数据
	 * @return $this
	 * @throws
	 */
	public function sourceData( $sourceData ){
		// 自动将对象转换成数组
		// @TODO :: 这里不需要转换
		$sourceData = com\Lib\Fun\Model::toArray( $sourceData );
		//
		$this->sourceData = $sourceData;
		return $this;
	}

	/**
	 * 连接关系绑定
	 * @param string $x 原字段
	 * @param string $tox 目标字段。[默认值：'id']
	 * @return $this
	 */
	public function aToB( string $x, string $tox = 'id' ){
		$this->joinConfig['atob'] = [ $x, $tox ];
		return $this;
	}

	/**
	 * 连接关系绑定->表A字段
	 * @param string $fieldName 字段名。['id'][默认]
	 * @return $this
	 */
	public function setJoinFieldA( string $fieldName = 'id' ) {
		$this->joinConfig['atob'][0] = $fieldName;
		return $this;
	}

	/**
	 * 连接关系绑定->表B字段
	 * @param string $fieldName 字段名。['id'][默认]
	 * @return $this
	 */
	public function setJoinFieldB( string $fieldName = 'id' ) {
		$this->joinConfig['atob'][1] = $fieldName;
		return $this;
	}

	/**
	 *
	 * @param string $is_one
	 * @return $this
	 */
	/*
	public function sourceOne( $is_one = true ){
		$this->joinConfig['sourceOne'] = $is_one;
		return $this;
	}
	*/

	/**
	 * 原数据是否为多行数据
	 * @param bool $isMore 是否为多行数据。[null]自动判断 [false]否 [true：默认]是
	 * @return $this
	 */
	public function sourceMore( $isMore = true ){
		$this->joinConfig['sourceOne'] = ! $isMore;
		return $this;
	}

	/**
	 * 获取echo_site
	 * @return string|null
	 */
	public function getEchoSite(  ) {
		return $this->joinConfig['echoSite'];
	}

	/**
	 * echo_site
	 * @param string $echoSite 链接数据保存路径（默认保存在"join_{A}"）。默认是相对路径，而以"->"开头是绝对路径(指向每组数据的根目录)
	 * @return $this
	 */
	public function setEchoSite( $echoSite = NULL ){
		$this->joinConfig['echoSite'] = $echoSite;
		return $this;
	}

	/**
	 * 默认连接数据（如果连接的数据为空，则使用默认数据）
	 * @param mixed $defaultData 默认数据
	 * @return $this
	 */
	public function defaultData( $defaultData = [] ){
		$this->joinConfig['defaultData'] = $defaultData;
		return $this;
	}

	/**
	 * 默认连接数据（如果连接的数据为空，则使用默认数据）
	 * @param mixed $defaultDatas 默认数据
	 * @return $this
	 */
	public function defaultDatas( $defaultDatas = [] ){
		$this->joinConfig['defaultDatas'] = $defaultDatas;
		return $this;
	}

	//show_data
	/**
	 *
	 * @param string $isShow
	 * @return $this
	 */
	public function showData( $isShow = true ){
		$this->joinConfig['showData'] = $isShow;
		return $this;
	}

	/**
	 * 设置"是否输出join的sql"
	 * @param bool $isEcho 是否输出join的sql
	 * @return $this
	 */
	public function showSql( $isEcho = true ){
		$this->joinConfig['showJoinSql'] = $isEcho;
		return $this;
	}

	/**
	 * 设置"是否使用从数据库查询"
	 * @param int $isConnectOnlyRead 是否使用从数据库查询。[false]否 [true][默认]是
	 * @return $this
	 */
	public function connectOnlyRead( $isConnectOnlyRead = true ){
		$this->joinConfig['is_connect_only_read'] = $isConnectOnlyRead;
		return $this;
	}

	/**
	 * 设置"是否待连接表中不存在的值"
	 * @param bool $isIgnore 是否忽略。[false]否 [true][默认]是
	 * @return $this
	 */
	public function ignoreNoExistA( $isIgnore = true ){
		$this->joinConfig['ignoreNoExistA'] = $isIgnore;
		return $this;
	}

	/**
	 * 设置"是否自动删除join数据为空的源数据"
	 * @param bool $isDelete 是否自动删除。[false]否 [true][默认]是
	 * @return $this
	 */
	public function isDeleteSourceOfJoinEmpty( $isDelete = true ){
		$this->joinConfig['isDeleteSourceOfJoinEmpty'] = $isDelete;
		return $this;
	}

	/**
	 * 设置"是否删除B表引用id"
	 * @param bool $isDelete 是否自动删除。[false]否 [true][默认]是
	 * @return $this
	 */
	public function isDeleteBQuoteId( $isDelete = true ){
		$this->joinConfig['isDeleteBQuoteId'] = $isDelete;
		return $this;
	}

	/**
	 * 设置"单条B表数据回调"
	 * @param \Closure $callFun 回调函数fun( $data )（自动传入一个准备join的B表$data数据，回调函数返回的数据作为新数据进行join）
	 * @return $this
	 */
	public function setJoinBDataCallfun( \Closure $callFun ){
		$this->joinConfig['joinBDataCallfun'] = $callFun;
		return $this;
	}

	/**
	 * [已弃用]设置"返回的数据类型"
	 * @param string $join_data_type 返回数据类型。 ['obj']对象 ['array']数组
	 * @return $this
	 */
	/*
	public function joinDataType( $join_data_type ){
		if( ! in_array( $join_data_type, [ 'obj', 'array' ] ) ) throw com\Lib\Err\Exception::makeOfNew( '', com\Cla\Err\Code::$sysFunParamError );
		$this->joinConfig['joinDataType'] = $join_data_type;
		return $this;
	}
	*/
	/**
	 * 设置"连接数据是否转为数组“
	 * @param bool $isToArray 是否转为数组。[false]转为对象 [true]转为数组
	 * @return $this
	 */
	public function joinToArray( $isToArray = true ){
		$this->joinConfig['joinDataType'] = $isToArray ? 'array' : 'obj';
		return $this;
	}

	/**
	 * 设置"连接表query回调函数"
	 * @param \Closure $callFun 回调函数（自动传入一个$query对象）
	 * @return $this
	 */
	public function joinTableQuery( \Closure $callFun ){
		$this->joinConfig['joinTableQueryCallFun'] = $callFun;
		return $this;
	}

	/**
	 * 需要获取的连接表的字段
	 * @param array|string $joinField 需要获取的连接表的字段
	 * @return $this
	 */
	public function joinTableField( $joinField = null ){
		$this->joinConfig['joinField'] = $joinField;
		return $this;
	}

	/**
	 * 开始连接（join单条数据）
	 * @param com\Model\Base\DataUnit|array|string $joinTable [array]数据 [string]连接表名（支持驼峰和下划线） 模型路径（可传入完整的命名空间路径，默认命名空间为：\App\Base\Base\）
	 * @param $atobB $atob_b
	 * @param array $joinField 连接关系绑定->目标字段。[默认值：'id']
	 * @return array 要获取的连接结构数据
	 */
	public function joinOne( $joinTable, string $atobB = 'id', ?array $joinField = null ){
		$this->joinConfig['joinOne'] = 1;
		return $this->joinMore( $joinTable, com\Lib\Fun\Str::parseName( $atobB ), $joinField );
	}

	/**
	 * 开始连接（join多条数据）
	 * @param com\Model\Base\DataUnit|array|string $joinTable [array]数据 [string]连接表名（支持驼峰和下划线）模型路径（可传入完整的命名空间路径，默认命名空间为：\App\Base\Base\）
	 * @param $atobB $atob_b
	 * @param array $joinField 连接关系绑定->目标字段。[默认值：'id']
	 * @return array 要获取的连接结构数据
	 */
	public function joinMore( $joinTable, string $atobB = 'id', ?array $joinField = null ){
		//
		$sourceData = $this->joinConfig['sourceOne'] ? [ $this->sourceData ] : $this->sourceData;
		// 设置参数->表名
		$this->setJoinTableInfo( $joinTable );
		//
		$this->setJoinFieldB( $atobB );
		// 设置参数->需要获取的连接表的字段
		if( $joinField !== null ) $this->joinTableField( $joinField );
		// 过滤需要where的数据
		/*		if( $this->isCallFilterSourceJoinData() ){
					$sourceData = $this->getFilterSourceJoinData( $sourceData );
				}
				if( empty( $sourceData ) ) return [];*/
		// 获取源表待join数据
		$preprocessingInfo = $this->extractSourceDataPreprocessing( $sourceData, $this->joinConfig['atob'][0] );
		// 获取join表对象数据
		$bTableData = $this->selectJoinTableObjData( $preprocessingInfo['allDataIds'] );
		// B数据joinA
		$this->joinResData = $this->joinAToBData( $sourceData, $preprocessingInfo['waitJoinPathQuote'], $bTableData );
		//
		// 调试语句
		if( $this->joinConfig['showData'] ){
			de( $this->joinResData );
		}
		//
		return $this->joinConfig['sourceOne'] ? $this->joinResData[0] ?? null : $this->joinResData;
	}
	/*
		public function cleanData( array &$sourceData ) {
			foreach( $sourceData as $sourceValue ){
				if(){

				}
			}

			if( $this->joinConfig['isDeleteSourceOfJoinEmpty'] === true ){
				d( $waitJoinPathQuoteValue );
				com\Lib\Fun\Array_::assignPathUnset( $waitJoinPathQuoteValue['keyQuote'], $waitJoinPathQuoteValue['keyPath'] );
				if( ! $this->joinConfig['useBIdToJoinArrayKey'] ){
					$keyPathSuperior = substr( $waitJoinPathQuoteValue['keyPath'], 0, strrpos( $waitJoinPathQuoteValue['keyPath'], '->' ) );
					$waitResetIndexArrayQuote[] = &com\Lib\Fun\Array_::extractAssignValue( $waitJoinPathQuoteValue['keyQuote'], $keyPathSuperior, [ 'returnQuote' => 1 ] );
				}
				continue;
			}else{

			}
		}
	*/
	/**
	 * 设置需要连接的表名信息
	 * @param com\Model\Base\DataUnit|array|string $joinTable 表模型
	 */
	public function setJoinTableInfo( $joinTable ){
		// $this->joinConfig['join_table_name'] = $joinTable->goModelQuery()->getModel()->getKeyName();
		// 检查类型
		if( ! is_array( $joinTable ) && ! ( $joinTable instanceof com\Model\Base\DataUnit ) && is_string( $joinTable ) ){
			throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$sysDataError );
		}
		//
		$this->joinConfig['joinTableInfo'] = $joinTable;
	}

	/**
	 * 获取查询对象
	 * @return com\Model\Base\DataUnit
	 */
	private function getJoinTableInfo(  ) {
		return $this->joinConfig['joinTableInfo'];
	}

	/**
	 * 读取join表需要的数据
	 * @param array $source_data_value_array
	 * @return array
	 */
	protected function selectJoinTableObjData( array $source_data_value_array ):array {
		if( empty( $source_data_value_array ) ){
			return [];
		}
		//
		$joinTableInfo = $this->getJoinTableInfo();
		if( is_array( $joinTableInfo ) ){
			return $joinTableInfo;
		}else if( $joinTableInfo instanceof com\Model\Base\DataUnit ){
			$joinTableQuery = $joinTableInfo;
		}else if( is_string( $joinTableInfo ) ){
			$joinTableQuery = new $joinTableInfo;
		}
		//
		// $join_table_query->where( $this->joinConfig['bFieldPrefix'] . $this->joinConfig['atob'][1], 'in', $source_data_value_str );
		$joinTableQuery->goModelQuery()->whereIn( $joinTableQuery->getTableName() . '.' . $this->joinConfig['atob'][1], $source_data_value_array );
		// 执行查询闭包
		if ( $this->joinConfig['joinTableQueryCallFun'] instanceof \Closure ) {
			$this->joinConfig['joinTableQueryCallFun']( $joinTableQuery );
		}
		// 设置field
		if( $this->joinConfig['joinField'] != '*' && $this->joinConfig['joinField'] !== [ '*' ] ){
			$joinTableQuery->addField( $this->joinConfig['joinField'], isRetain: false );
		}
		$joinTableQuery->hasFieldOrAdd( $this->joinConfig['atob'][1], true, false );
		/*
		$queryField = $joinTableQuery->goModelQuery()->getQuery()->columns;
		if( $queryField !== null ){
			// 格式化
			if( $joinTableQuery instanceof com\Model\Base\DataUnit ){
				foreach ( $queryField as &$queryFieldValue ){
					$queryFieldValue = $joinTableQuery->getFieldActualName( $queryFieldValue );
				} unset( $queryFieldValue );
			}
			//
			if( array_search( $this->joinConfig['atob'][1], $queryField ) === false ){
				$joinTableQuery->addField( $this->joinConfig['atob'][1] );
			}else {
				$this->isDeleteBQuoteId( false );
			}
		}
		*/
		// 判断是否输出sql
		if( $this->joinConfig['showJoinSql'] ){
			de($joinTableQuery->getSql());
		}
		// 获取数据
		$data = $joinTableQuery->select()->toArray();
		//
		return $data;
	}

	/**
	 * 提取源数据需要join的数据值
	 * @param array $source_data
	 * @param string $source_key_path
	 * @return array 源数据id列表 [ 1, 2, 3 ]
	 */
	/*
	protected function extractSourceDataValueArray( array $source_data, string $source_key_path ):array {
		if( $this->joinConfig['sourceOne'] === null ) $this->joinConfig['sourceOne'] = $this->judgeSourceOneType( $source_data );
		// 过滤需要where的数据
		if( $this->isCallFilterSourceJoinData() ) $source_data = $this->getFilterSourceJoinData( $source_data );
		if( empty( $source_data ) ) return [];
		//
		if( $this->joinConfig['sourceOne'] ){
			$value_data = [
				(string)com\Lib\Fun\Array_::extractAssignValue( $source_data, $source_key_path )
			];
		}else{
			$value_data = com\Lib\Fun\Array_::extractAllAssignValue( $source_data, $source_key_path );
		}
		//
		return $value_data;
	}
	*/

	/**
	 * 源数据表预处理
	 * @param array $sourceData
	 * @param string $sourceKeyPath
	 * @return array
	 */
	private function extractSourceDataPreprocessing( array &$sourceData, string $sourceKeyPath ) {
		//
		$sourceKeyPathArray = explode( '->', com\Lib\Fun\Array_::formatKeyPathMultilayer( $sourceKeyPath ) );
		// 遍历出所有需要join的路径
		$pathsQuote = &com\Lib\Fun\Array_::extractAllPathQuote( $sourceData, $sourceKeyPath );
		// 准备工作
		$allDataIds = [];
		$waitJoinPathQuote = [];
		$lastKeyPathIsArray = substr( end( $sourceKeyPathArray ), 0, 2 ) === '[]';
		if( $lastKeyPathIsArray ){
			$lastKeyPath = substr( end( $sourceKeyPathArray ), 2 );
		}else{
			$lastKeyPath = end( $sourceKeyPathArray );
		}
		// 初始化 echoSite 相关数据
		$echoSite = $this->getEchoSite() ?? com\Lib\Fun\Str::parseName( "join_{$lastKeyPath}", 1, false );
		// 判断是否为相对路径
		$echoSiteIsRelative = substr( $echoSite, 0, 2 ) !== '->';
		if( ! $echoSiteIsRelative ){
			// 绝对目录去掉开头
			$echoSite = substr( $echoSite, 2 );
		}
		// 格式化 echoSite，将 "a->[]" 转为 "a[]"
		$echoSite = str_replace( '->[]', '[]', $echoSite );
		if( ! $echoSiteIsRelative || $lastKeyPathIsArray ){
			if( strpos( $echoSite, '[{id}]' ) === false && strpos( $echoSite, '[]' ) === false ){
				if( $this->joinConfig['useBIdToJoinArrayKey'] ){
					$echoSite .= '[{id}]';
				}else if( $this->joinConfig['joinOne'] !== 1 ){
					$echoSite .= '[]';
				}
			}
		}
		// 检查$echoSite是否存在多个[]
		$echoSiteManyCountNum = substr_count( $echoSite, '[]' );
		// $echoSiteIsMany = $echoSiteManyCountNum === 1;
		if( $echoSiteManyCountNum > 1 ){
			throw com\Lib\Err\Exception::makeOfNew( '输出路径最多只允许存在一个[]', com\Lib\Err\Code::$sysFunParamError );
		}
		//
		foreach( $pathsQuote as &$pathsVlaue ){
			// 如果不存在则跳过
			if( $lastKeyPathIsArray ){
				if( empty( $pathsVlaue['valueQuote'] ) ){
					continue;
				}
			}else{
				if( ! isset( $pathsVlaue['valueQuote'][ $lastKeyPath ] ) ){
					continue;
				}
			}
			//
			if( $this->isFilterSourceJoinData( $pathsVlaue['rootQuote'], $pathsVlaue['valueQuote'] ) !== true ){
				continue;
			}
			//
			$keyQuote = null;
			if( $echoSiteIsRelative ){
				// 获取需要赋值的源数据 数据指针
				$keyQuote = &$pathsVlaue['valueQuote'];
			}else{
				$keyQuote = &$pathsVlaue['rootQuote'];
			}
			//
			if( $lastKeyPathIsArray ){
				// 处理字符串数组
				$dataIds = com\Lib\Fun\Array_::toArray( $pathsVlaue['valueQuote'] );
				$allDataIds = array_merge( $allDataIds, $dataIds );
				foreach( $dataIds as $dataIdsValue ){
					$newEchoSite = $this->initNewEchoSite( $keyQuote, $echoSite, $dataIdsValue );
					//
					$waitJoinPathQuote[] = [
						'id' => $dataIdsValue,
						'rootKey' => $pathsVlaue['rootKey'],
						'keyQuote' => &$keyQuote,
						// 'keyPath' => $echoSite . '->' . ( count( $source_value_quote ) - 1 ),
						'keyPath' => $newEchoSite,
					];
				}
			}else{
				// 普通处理
				/*
				$newEchoSite = $echoSite;
				if( $echoSiteIsMany ){
					$newEchoSite = $this->initNewEchoSite( $keyQuote, $echoSite );
				}else{
					// 获取需要赋值的源数据 数据指针
					com\Lib\Fun\Array_::assignPathWrite( $keyQuote, $echoSite, null );
				}
				*/
				//
				$newEchoSite = $this->initNewEchoSite( $keyQuote, $echoSite, $pathsVlaue['valueQuote'][ $lastKeyPath ] );
				//
				$allDataIds[] = $pathsVlaue['valueQuote'][ $lastKeyPath ];
				//
				$waitJoinPathQuote[] = [
					'id' => $pathsVlaue['valueQuote'][ $lastKeyPath ],
					'rootKey' => $pathsVlaue['rootKey'],
					'keyQuote' => &$keyQuote,
					'keyPath' => $newEchoSite,
				];
			}
			//
			unset( $keyQuote ); // 销毁是为了创建不同新的变量地址，防止循环时一直使用同一个变量地址
		}
		// 如无数据时，建立数组
		if( empty( $waitJoinPathQuote ) && ! $echoSiteIsRelative ){
			$this->initAbsEchoSiteDefault( $sourceData, $echoSite );
			// $this->joinConfig['joinOne']
		}
		//
		return [
			'allDataIds' => $allDataIds,
			'waitJoinPathQuote' => $waitJoinPathQuote,
		];
	}

	/**
	 * 初始化绝对路径为空数组
	 * @param array &$sourceData
	 * @param string $echoSite
	 * @return void
	 */
	private function initAbsEchoSiteDefault( array &$sourceData, string $echoSite ):void {
		$echoSiteManySite = strpos( $echoSite, '[' );
		$newEchoSite = $echoSiteManySite === false ? $echoSite : substr( $echoSite, 0, $echoSiteManySite );
		foreach( $sourceData as &$sourceDataValue ){
			$echoSiteManyData = &com\Lib\Fun\Array_::extractAssignValue( $sourceDataValue, $newEchoSite, [ 'is_strict' => 0, 'returnQuote' => 1, 'default' => '' ] );
			if( empty( $echoSiteManyData ) ){
				$echoSiteManyData = [];
			}
		}

	}

	/**
	 * 初始化并返回新的EchoSite地址
	 * @param array $keyQuote
	 * @param string $echoSite
	 * @return string EchoSite
	 */
	private function initNewEchoSite( array &$keyQuote, string $echoSite, $assignKey ):string {
		// 处理[{id}]
		$echoSiteManySite = strpos( $echoSite, '[{id}]' );
		if( $echoSiteManySite !== false ){
			$newEchoSite = str_replace( '[{id}]', "->{$assignKey}", $echoSite );
			$newEchoSite = str_replace( '->->', "->", $newEchoSite );
			com\Lib\Fun\Array_::assignPathWrite( $keyQuote, $newEchoSite, null );
			return $newEchoSite;
		}
		//
		// 处理[]
		com\Lib\Fun\Array_::assignPathWrite( $keyQuote, $echoSite, null );
		$echoSiteManySite = strpos( $echoSite, '[]' );
		if( $echoSiteManySite !== false ){
			$echoSiteManyData = com\Lib\Fun\Array_::extractAssignValue( $keyQuote, substr( $echoSite, 0, $echoSiteManySite ) );
			$echoSiteManyCount = count( $echoSiteManyData );
			//
			$newEchoSite = substr( $echoSite, 0, $echoSiteManySite );
			$newEchoSite = rtrim( $newEchoSite, '->' ); // 消除结尾多余的"->"
			$newEchoSite .= "->" . ( $echoSiteManyCount - 1 );
			if( strlen( $echoSite ) >= ( $echoSiteManySite + 2 ) ){
				$newEchoSite .= substr( $echoSite, $echoSiteManySite + 2 );
			}
			return $newEchoSite;
		}
		//
		return $echoSite;

	}

	/**
	 * 判断"source_one"类型
	 * @param array $source_data
	 * @return int 返回"source_one"类型。[0]单维数组 [1]多维数组
	 */
	/*
	protected function judgeSourceOneType( array $source_data ){
		$index_i = 0;
		$source_one_type = 0;
		foreach ( $source_data as $source_key => $source_value ){
			if( ! is_numeric( $source_key ) ){
				$source_one_type = 1;
				break;
			}
			if( $index_i++ > 10 ) break;
		}
		//
		return $source_one_type;
	}
	*/

	/**
	 * B数据joinA
	 * @param array $sourceData
	 * @param array $waitJoinPathQuote
	 * @param array $bTableData B表数据
	 * @return array B数据joinA的结果数据
	 */
	protected function joinAToBData( array &$sourceData, array $waitJoinPathQuote, array $bTableData ){
		//生成索引数据（提高匹配速度）
		$joinDataIndex = [];
		foreach ( $bTableData as &$join_table_obj_value ){
			if( $this->joinConfig['joinDataType'] == 'array' && ! is_array( $join_table_obj_value ) ) $join_table_obj_value = $join_table_obj_value->toArray();
			if( $this->joinConfig['joinOne'] ){
				$joinDataIndex[ $join_table_obj_value[ $this->joinConfig['atob'][1] ] ] = &$join_table_obj_value;
			}else{
				$joinDataIndex[ $join_table_obj_value[ $this->joinConfig['atob'][1] ] ][] = &$join_table_obj_value;
			}
		}
		//
		$waitResetIndexArrayQuote = [];
		foreach( $waitJoinPathQuote as $waitJoinPathQuoteValue ){

			$waitJoinDataId = $waitJoinPathQuoteValue['id'];
			$joinDataIndex[ $waitJoinDataId ] = $joinDataIndex[ $waitJoinDataId ] ?? null;

			/*
			 // 检查数据是否需要join
						$joinDefaultData = $this->getNeedSourceJoinDataAndDefaultData( $joinDataIndex[ $waitJoinDataId ] );
						$joinData = null;
						if( $joinDefaultData !== true ){ // 不需要
							if( $joinDataIndex[ $waitJoinDataId ] !== null ) continue; // 如果值已存在，则不覆盖
							// 赋值
							if( $joinDefaultData == null ) continue; // 没有默认值
							$joinData = $joinDefaultData;
						}else{
							// 获取赋值的数据
							$joinData = $joinDataIndex[ $waitJoinDataId ] ?? $this->joinConfig['defaultData'];
							if( $this->joinConfig['join_before_b_data_callfun'] instanceof \Closure ){
								$joinData = $this->joinConfig['join_before_b_data_callfun']( $joinData );
							}
						}*/
			// 获取赋值的数据
			$joinData = $joinDataIndex[ $waitJoinDataId ] ?? null;
			// 回调
			if( $this->joinConfig['joinBDataCallfun'] instanceof \Closure ){
				$joinData = $this->joinConfig['joinBDataCallfun']( $joinData );
			}else if( $joinData === null ){
				// 使用默认数据
				if( $this->joinConfig['joinOne'] ){
					$joinData = $this->joinConfig['defaultData'];
				}else{
					// more的默认数据为[]
					$joinData = $this->joinConfig['defaultDatas'];
				}
			}
			//
			// 是否自动删除join数据为空的源数据
			if( empty( $joinData ) ){
				if( $this->joinConfig['isDeleteSourceOfJoinEmpty'] === true ){
					unset( $sourceData[ $waitJoinPathQuoteValue['rootKey'] ] );
					continue;
				}
				/*
				// @todo :: 不知道有什么用，先留着
				if( $this->joinConfig['isDeleteSourceOfJoinEmpty'] === true ){
					com\Lib\Fun\Array_::assignPathUnset( $waitJoinPathQuoteValue['keyQuote'], $waitJoinPathQuoteValue['keyPath'] );
					if( ! $this->joinConfig['useBIdToJoinArrayKey'] ){
						$keyPathSuperior = substr( $waitJoinPathQuoteValue['keyPath'], 0, strrpos( $waitJoinPathQuoteValue['keyPath'], '->' ) );
						$waitResetIndexArrayQuote[] = &com\Lib\Fun\Array_::extractAssignValue( $waitJoinPathQuoteValue['keyQuote'], $keyPathSuperior, [ 'returnQuote' => 1 ] );
					}
					continue;
				}
				*/
				//
				com\Lib\Fun\Array_::assignPathWrite( $waitJoinPathQuoteValue['keyQuote'], $waitJoinPathQuoteValue['keyPath'], null );
			}
			// 清理a表引用id
			if( $this->joinConfig['isDeleteAQuoteId'] ){
				if( is_array( $joinData ) ){
					unset( $waitJoinPathQuoteValue['keyQuote'][ $this->joinConfig['atob'][0] ] );
				}
			}
			// 清理b表引用id
			if( $this->joinConfig['isDeleteBQuoteId'] ){
				if( is_array( $joinData ) ){
					// 检查是否为强制保留字段
					$joinTableInfo = $this->getJoinTableInfo();
					if( $joinTableInfo instanceof com\Model\Base\DataUnit ){
						if( ! $joinTableInfo->hasMustRetainField( $this->joinConfig['atob'][1] ) ){
							unset( $joinData[ $this->joinConfig['atob'][1] ] );
						}
					}else{
						unset( $joinData[ $this->joinConfig['atob'][1] ] );
					}
				}
			}
			// 赋值
			// $waitJoinPathQuoteValue = $joinDataIndex[ $waitJoinDataId ];
			com\Lib\Fun\Array_::assignPathWrite( $waitJoinPathQuoteValue['keyQuote'], $waitJoinPathQuoteValue['keyPath'], $joinData );
			// com\Lib\Fun\Array_::assignPathWrite( $waitJoinPathQuoteValue['keyQuote'], $waitJoinPathQuoteValue['keyPath'], $joinDataIndex[ $waitJoinDataId ] );
		} unset( $waitJoinPathQuoteValue );
		// 索引重新排序
		foreach( $waitResetIndexArrayQuote as &$waitResetIndexArrayQuoteValue ){
			$waitResetIndexArrayQuoteValue = array_values( $waitResetIndexArrayQuoteValue );
		}
		//
		return $sourceData;
	}

	// ===============================
	//
	//
	// 下面为静态方法
	//
	//
	// ===============================

	/**
	 * @param array|null $sourceData
	 * @return $this
	 */
	public static function makeOfNew( array|null $sourceData = null ):self {
		return self::make( $sourceData );
	}

}
